﻿using UnityEngine.Sprites;

namespace UnityEngine.UI
{
	[RequireComponent( typeof( CanvasRenderer ) )]
	public class UVImage : Image
	{
		public enum FillMode { Inside = 0, Outside = 1, Edge = 2 };
		public enum ShapeMode { Circle = 0, Rect = 1};

#pragma warning disable 0649
		[SerializeField] private FillMode fill;
		[SerializeField] private ShapeMode shape;
		[SerializeField] private int segments = 16;
		[SerializeField] private Vector2 uvPivot = new Vector2(0.5f,0.5f);
		[SerializeField] private float scale = 1;
		[Tooltip("边缘厚度")][SerializeField] private float edge = 1;
#pragma warning restore 0649
		protected override void OnPopulateMesh( VertexHelper vh )
		{
			if (!overrideSprite)
			{
				base.OnPopulateMesh(vh);
				return;
			}
			vh.Clear();
			if (shape == ShapeMode.Circle)
				GenerateShapeCircle(vh);
			else if(shape == ShapeMode.Rect)
				GenerateShapeRect(vh);
		}

		private void GenerateShapeCircle(VertexHelper vh)
		{
			Rect drawingDimensions = this.GetPixelAdjustedRect();
			Vector4 packedUv = !((UnityEngine.Object) this.sprite != (UnityEngine.Object) null) ? Vector4.zero : DataUtility.GetOuterUV(this.sprite);
			float width = drawingDimensions.width;
			float height = drawingDimensions.height;
			
			Vector2 pivot = rectTransform.pivot;
			pivot = new Vector2(width * ( 0.5f - pivot.x ),height * ( 0.5f - pivot.y ));
			Rect rect = new Rect(pivot.x, pivot.y, width*0.5f, height*0.5f);
			float whp = height / width;
			if( fill == FillMode.Inside )
			{
				Vector4 padding = DataUtility.GetPadding(this.sprite);
				//(x=left, y=bottom, z=right, w=top).
				Vector2 textureSize = new Vector2(sprite.texture.width,sprite.texture.height);
				packedUv.x -= Mathf.Round(padding.x) / textureSize.x;
				packedUv.y -= Mathf.Round(padding.y) / textureSize.y;
				packedUv.z += Mathf.Round(padding.z) / textureSize.x;
				packedUv.w += Mathf.Round(padding.w) / textureSize.y;
				Vector2 pivotUV = new Vector2( Mathf.Lerp(packedUv.x,packedUv.z,uvPivot.x),Mathf.Lerp( packedUv.y, packedUv.w,uvPivot.y)); // uv center point
				float detailRad = 360f / segments * Mathf.Deg2Rad;
				vh.AddVert( new Vector3( rect.x, rect.y, 0f ), color, pivotUV );
				Vector2 uvPixel = new Vector2(this.sprite.rect.width / textureSize.x / scale, this.sprite.rect.height / textureSize.y / scale);
				for (int i = 0; i <= segments; i++)
				{
					float radians = i * detailRad;
					float cos = Mathf.Cos(radians);
					float sin = Mathf.Sin(radians);
					Vector2 uv = new Vector2(pivotUV.x + uvPixel.x * cos, pivotUV.y + uvPixel.y * sin  * whp);
					vh.AddVert( new Vector3(rect.x + rect.width * cos , rect.y + rect.height * sin , 0f ), color,uv);
					if (vh.currentVertCount > 2)
					{
						int indx = i + 1;
						vh.AddTriangle( indx, indx - 1, 0 );
					}
				}
			}
			else if( fill == FillMode.Outside )
			{
				Vector2 pivotUV = new Vector2( packedUv.x + packedUv.z, packedUv.y + packedUv.w) * 0.5f; // uv center point
				int quarterDetail = ( segments + 3 ) / 4;
				float detailRad = 360f / ( quarterDetail * 4 ) * Mathf.Deg2Rad;

				vh.AddVert( new Vector3( width  + pivot.x, height  + pivot.y, 0f ), color, pivotUV );
				vh.AddVert( new Vector3( -width + pivot.x, height  + pivot.y, 0f ), color, pivotUV );
				vh.AddVert( new Vector3( -width + pivot.x, -height + pivot.y, 0f ), color, pivotUV );
				vh.AddVert( new Vector3( width  + pivot.x, -height + pivot.y, 0f ), color, pivotUV );

				int triangleIndex = 4;
				FillOutside( vh,rect,packedUv, new Vector3( width + pivot.x, pivot.y, 0f ), 0, quarterDetail, ref triangleIndex );
				FillOutside( vh,rect,packedUv, new Vector3( pivot.x, height + pivot.y, 0f ), 1, quarterDetail, ref triangleIndex );
				FillOutside( vh,rect,packedUv, new Vector3( -width + pivot.x, pivot.y, 0f ), 2, quarterDetail, ref triangleIndex );
				FillOutside( vh,rect,packedUv, new Vector3( pivot.x, -height + pivot.y, 0f ), 3, quarterDetail, ref triangleIndex );
			}
			else
			{
				Vector2 pivotUV = new Vector2( packedUv.x + packedUv.z, packedUv.y + packedUv.w) * 0.5f; // uv center point
				float detailRad = 360f / segments * Mathf.Deg2Rad;
				float innerWidth = rect.width - edge;
				float innerHeight = rect.height - edge;

				vh.AddVert( new Vector3( rect.width + rect.x, rect.y, 0f ), color, pivotUV );
				vh.AddVert( new Vector3( innerWidth + rect.x, rect.y, 0f ), color, pivotUV );

				int triangleIndex = 2;
				for( int i = 1; i < segments; i++, triangleIndex += 2 )
				{
					float radians = i * detailRad;
					float cos = Mathf.Cos( radians );
					float sin = Mathf.Sin( radians );

					vh.AddVert( new Vector3( cos * rect.width + rect.x, sin * rect.height + rect.y, 0f ), color, pivotUV );
					vh.AddVert( new Vector3( cos * innerWidth + rect.x, sin * innerHeight + rect.y, 0f ), color, pivotUV );

					vh.AddTriangle( triangleIndex, triangleIndex - 2, triangleIndex - 1 );
					vh.AddTriangle( triangleIndex, triangleIndex - 1, triangleIndex + 1 );
				}

				vh.AddTriangle( 0, triangleIndex - 2, triangleIndex - 1 );
				vh.AddTriangle( 0, triangleIndex - 1, 1 );
			}
		}
		private void GenerateShapeRect(VertexHelper vh)
		{
			Rect drawingDimensions = this.GetPixelAdjustedRect();
			Vector4 packedUv = !((UnityEngine.Object) this.sprite != (UnityEngine.Object) null) ? Vector4.zero : DataUtility.GetOuterUV(this.sprite);
			float width = drawingDimensions.width;
			float height = drawingDimensions.height;

			Vector2 pivot = rectTransform.pivot;
			pivot = new Vector2(width * ( 0.5f - pivot.x ),height * ( 0.5f - pivot.y ));
			Rect rect = new Rect(pivot.x, pivot.y, width*0.5f, height*0.5f);
			float whp = height / width;
			if( fill == FillMode.Inside )
			{
				Vector4 padding = DataUtility.GetPadding(this.sprite);
				//(x=left, y=bottom, z=right, w=top).
				Vector2 textureSize = new Vector2(sprite.texture.width,sprite.texture.height);
				packedUv.x -= Mathf.Round(padding.x) / textureSize.x;
				packedUv.y -= Mathf.Round(padding.y) / textureSize.y;
				packedUv.z += Mathf.Round(padding.z) / textureSize.x;
				packedUv.w += Mathf.Round(padding.w) / textureSize.y;
				Vector2 pivotUV = new Vector2( Mathf.Lerp(packedUv.x,packedUv.z,uvPivot.x),Mathf.Lerp( packedUv.y, packedUv.w,uvPivot.y)); // uv center point
				Vector2 uvPixel = new Vector2(this.sprite.rect.width / textureSize.x / scale, this.sprite.rect.height / textureSize.y / scale);
				
				vh.AddVert(new Vector3(rect.x - rect.width, rect.y - rect.height), color, 
					new Vector2(Mathf.Clamp(pivotUV.x - uvPixel.x,packedUv.x,packedUv.z), Mathf.Clamp(pivotUV.y - uvPixel.y * whp,packedUv.y,packedUv.w)));
				vh.AddVert(new Vector3(rect.x - rect.width, rect.y + rect.height), color, 
					new Vector2(Mathf.Clamp(pivotUV.x - uvPixel.x,packedUv.x,packedUv.z), Mathf.Clamp(pivotUV.y + uvPixel.y * whp,packedUv.y,packedUv.w)));
				vh.AddVert(new Vector3(rect.x + rect.width, rect.y + rect.height), color, 
					new Vector2(Mathf.Clamp(pivotUV.x + uvPixel.x,packedUv.x,packedUv.z), Mathf.Clamp(pivotUV.y + uvPixel.y * whp,packedUv.y,packedUv.w)));
				vh.AddVert(new Vector3(rect.x + rect.width, rect.y - rect.height), color, 
					new Vector2(Mathf.Clamp(pivotUV.x + uvPixel.x,packedUv.x,packedUv.z), Mathf.Clamp(pivotUV.y - uvPixel.y * whp,packedUv.y,packedUv.w)));
				vh.AddTriangle(0, 1, 2);
				vh.AddTriangle(2, 3, 0);
			}
		}
		public override void Cull( Rect clipRect, bool validRect )
		{
			canvasRenderer.cull = false;
		}

		private void FillOutside( VertexHelper vh,Rect rect,Vector4 packedUv, Vector3 initialPoint, int quarterIndex, int detail, ref int triangleIndex )
		{
			Vector2 pivotUV = new Vector2( packedUv.x + packedUv.z, packedUv.y + packedUv.w) * 0.5f; // uv center point
			float detailRad = 360f / detail * Mathf.Deg2Rad;
			int startIndex = quarterIndex * detail;
			int endIndex = ( quarterIndex + 1 ) * detail;

			vh.AddVert( initialPoint, color, pivotUV );
			triangleIndex++;

			for( int i = startIndex + 1; i <= endIndex; i++, triangleIndex++ )
			{
				float radians = i * detailRad;

				vh.AddVert( new Vector3( Mathf.Cos( radians ) * rect.width + rect.x, Mathf.Sin( radians ) * rect.height + rect.y, 0f ), color, pivotUV);
				vh.AddTriangle( quarterIndex, triangleIndex - 1, triangleIndex );
			}
		}

		
		
		private Vector4 GetDrawingDimensions(bool shouldPreserveAspect)
		{
			Vector4 vector4 = !((UnityEngine.Object) this.sprite == (UnityEngine.Object) null) ? DataUtility.GetPadding(this.sprite) : Vector4.zero;
			Vector2 spriteSize = !((UnityEngine.Object) this.sprite == (UnityEngine.Object) null) ? new Vector2(this.sprite.rect.width, this.sprite.rect.height) : Vector2.zero;
			Rect pixelAdjustedRect = this.GetPixelAdjustedRect();
			int num1 = Mathf.RoundToInt(spriteSize.x);
			int num2 = Mathf.RoundToInt(spriteSize.y);
			Vector4 drawingDimensions = new Vector4(vector4.x / (float) num1, vector4.y / (float) num2, ((float) num1 - vector4.z) / (float) num1, ((float) num2 - vector4.w) / (float) num2);
			if (shouldPreserveAspect && (double) spriteSize.sqrMagnitude > 0.0)
				this.PreserveSpriteAspectRatio(ref pixelAdjustedRect, spriteSize);
			drawingDimensions = new Vector4(pixelAdjustedRect.x + pixelAdjustedRect.width * drawingDimensions.x, pixelAdjustedRect.y + pixelAdjustedRect.height * drawingDimensions.y, pixelAdjustedRect.x + pixelAdjustedRect.width * drawingDimensions.z, pixelAdjustedRect.y + pixelAdjustedRect.height * drawingDimensions.w);
			return drawingDimensions;
		}
		
		private void PreserveSpriteAspectRatio(ref Rect rect, Vector2 spriteSize)
		{
			float num1 = spriteSize.x / spriteSize.y;
			float num2 = rect.width / rect.height;
			if ((double) num1 > (double) num2)
			{
				float height = rect.height;
				rect.height = rect.width * (1f / num1);
				rect.y += (height - rect.height) * this.rectTransform.pivot.y;
			}
			else
			{
				float width = rect.width;
				rect.width = rect.height * num1;
				rect.x += (width - rect.width) * this.rectTransform.pivot.x;
			}
		}

	}
}
